/***************************************************************************
 *	@file 		fetion_user.h
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		definition of class userlist, class fetionUser and 
 *				class currentUser, userlist is user list which is store on local
 *				disk for login. fetionUser is ordinary user of fetion, currentUser
 *				is current user has only instance in whole program.
 *	@modify		none
 ***************************************************************************/

#ifndef FETION_USER_H
#define FETION_USER_H

#include "fetion_verify_code.h"
#include "fetion_chat.h"
#include "fetion_exception.h"

typedef enum {
	P_ONLINE = 		 400 , 
	P_RIGHTBACK = 	 300 ,
	P_AWAY = 		 100 ,
	P_BUSY = 		 600 ,
	P_OUTFORLUNCH =  500 ,
	P_ONTHEPHONE = 	 150 ,
	P_MEETING = 	 850 ,
	P_DONOTDISTURB = 800 ,
	P_HIDDEN = 		 0 ,
	P_OFFLINE =      -1
} StateType;

typedef enum {
	STATUS_NORMAL = 1 ,					/* normal status											 */
	STATUS_OFFLINE ,					/* user offline , deleted you from his list or out of service*/
	STATUS_NOT_AUTHENTICATED ,			/* user has not accept your add buddy request				 */
	STATUS_SMS_ONLINE ,					/* user has not start fetion service						 */
	STATUS_REJECTED ,					/* user rejected your add buddy request,wait for deleting 	 */
	STATUS_SERVICE_CLOSED , 			/* user has closed his fetion service 						 */
	STATUS_NOT_BOUND					/* user doesn`t bound fetion number to a mobile number 		 */
} StatusType;

#define BOUND_MOBILE_DISABLE 0
#define BOUND_MOBILE_ENABLE 1
#define RELATION_STATUS_AUTHENTICATED   1
#define RELATION_STATUS_UNAUTHENTICATED 0

#define LOGIN_TYPE_FETIONNO    			1
#define LOGIN_TYPE_MOBILENO    			0
#define BASIC_SERVICE_NORMAL   			1
#define BASIC_SERVICE_ABNORMAL 			0
#define CARRIER_STATUS_OFFLINE			-1
#define CARRIER_STATUS_NORMAL  			0
#define CARRIER_STATUS_DOWN    			1
#define CARRIER_STATUS_CLOSED           2 

class userlist
{
public:
	typedef struct {
		std::string userNo;
		std::string passwd;
		std::string userId;
		std::string sId;
		int lastState;
		int isLastUser;
	} userinfo;

	userlist(const std::string &DBpath) : db_path(DBpath), item_num(6)
	{
		ulist.clear();
	}

	class equal_to_userNo	// function object
	{
	public:
		equal_to_userNo(const std::string &no) : _no(no) {}
		bool operator()(const userinfo &ui)
		{
			return ui.userNo == _no;
		}
	private:
		std::string _no;
	};

	int create_table_in_db();
	int load_from_database();
	std::vector<userinfo> ulist;
	userinfo *find_by_userNo(const std::string &no);
	void print();
	int update(userinfo &ui);
private:
	std::string db_path;
	const unsigned int item_num;
};

class fetionUser
{
public:
	friend class loginThreadCallable;
	friend class HashPassword;
	friend class SipProcSipc;
	fetionUser(const std::string &no) : mobileno(no) {}
	fetionUser() {}

protected:
	std::string userId;					/* userid used since v4 protocal      				*/
	std::string sId;					/* fetion no					      					*/
	std::string sipuri;					/* sipuri like 'sip:100@fetion.com.cn'				*/
	std::string nickname;				/* user`s nickname					    			*/
	std::string impression;				/* user`s mood phrase				    				*/
	std::string mobileno;				/* mobile phone number				    			*/
	std::string country;				/* user`s country`s simplified form,like CN 		*/
	std::string province;				/* user`s province`s simplified form,like bj		*/
	std::string city;					/* user`s city`s code ,like 10 for beijing			*/
	int state;							/* state type like online,busy,etc					*/
	int gender;							/* gender 1 for male 2 for female,0 for private	*/
};

class Config;
class Contact;
class FetionSip;
class Group;
class BuddyApply;
class Command;
class Conversation;

class currentUser : public fetionUser
{
public:
	friend class loginThreadCallable;
	friend class VerifyCode;
	friend class Config;
	friend class Register;
	friend class SipProcSipc;

	static currentUser *get_instance(const std::string &no = "", const std::string &passwd = "")
	{
		static currentUser current_user(no, passwd);
		return &current_user;
	}
	
public:
	~currentUser();
	
	bool Verified() 
	{
		return (ver != NULL) && (!ver->code.empty());
	}
	FetionSip *get_sip() { return sip; }
public:
	void parse_ssi_succ(const xmlpp::Node *pNode);
	void parse_ssi_fail(const xmlpp::Node *pNode);
	void get_userinfo(userlist::userinfo &ui);
	int get_contact_count()// 
	{
		return contactList.size();
	};
	void reorder_contactlist()// 
	{
		contactList.sort();
	}
	void add_contact(Contact &contact)
	{
		contactList.push_back(contact);
	}
	void add_tmp_contact(Contact &contact)
	{
		tmp_contactList.push_back(contact);
	}
public:
	int create_table_in_db();
	int load_from_database();
	int save_to_database();

public:	
	int load_contacts_from_db();// 
	int save_contacts_to_db();// 
	int get_sip_auth_body(std::string &body) const;
	void update_data() 
	{
		if(FUNC_SUCC != this->save_to_database()) {
			WRITE_LOG(LOG_WARNING, "save user information to database failed.");
		}
		if(FUNC_SUCC != this->save_contacts_to_db()) {
			WRITE_LOG(LOG_WARNING, "save contacts to database failed.");
		}
	}
	
public:
	void cleanup_group_list();
	void cleanup_contact_list();// 
	void print_contactlist();// 
	
public:
	int parse_add_buddy_verification(const std::string &sipres);// 
	int parse_personal_info(xmlpp::Node *pNode);
	int parse_contact_list(xmlpp::Node *pNode);// 
	int parse_stranger_list(xmlpp::Node *pNode);// 
	int parse_sms_frequency(xmlpp::Node *pNode); 
	int parse_option_verification();
//	int parse_presence_body(const std::string &body);

public:
	Group *find_group_by_id(const int id);
	Contact *find_contact_by_userid(const std::string &userid);// 
	Contact *find_contact_by_groupid(const int group_id);//
	Contact *find_contact_by_sipuri(const std::string &sip_uri);
	Contact *find_contact_by_string(const std::string &str);
	Contact *find_tmp_contact_by_string(const std::string &str);

public:
	int handle_add_buddy_request(Command &cmd);
	int send_message_to_contact(Command &cmd);
	int send_direct_sms(Command &cmd);

public:
	int get_contact_information_by_no(const std::string &no);

private:
	currentUser(const std::string &no, const std::string &passwd) :
		fetionUser(no), password(passwd), config(NULL),	applyList(NULL), 
		recentMessage(NULL), currentConv(NULL), ver(NULL), sip(NULL)
	{
		recentMessage = new Message;
		if(NULL == recentMessage) {
			throw fetionException("memory allocation failed.");
		}
		currentConv = new Conversation;
		if(NULL == currentConv) {
			throw fetionException("memory allocation failed.");
		}
	}
		
private:
	std::string password;
	std::string ssic;
	int loginStatus;
	int carrierStatus;

private:
	std::string publicIp;
	std::string lastLoginIp;
	std::string lastLoginTime;	
	std::string personalVersion;
	std::string contactVersion;
	std::string customConfigVersion;
	std::string smsOnLineStatus;
	std::string customConfig;
	int boundToMobile;

private:
	int smsDayLimit;
	int smsDayCount;
	int smsMonthLimit;
	int smsMonthCount;

public:
	Config *config;
	BuddyApply *applyList;
	Message *recentMessage;
	Conversation *currentConv;
	
private:
	Verification *ver;
	FetionSip *sip;
	unsigned int groupCount;
	std::list<Group> groupList;
	unsigned int contactCount;
	std::list<Contact> contactList;
	std::list<Contact> tmp_contactList;
};

#endif
